home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / program / vol16n13.zip / OPENTR.ZIP / OT_SRC.ZIP / OTVIEW.CPP < prev    next >
C/C++ Source or Header  |  1997-05-26  |  18KB  |  471 lines

  1. // OTView.cpp : implementation of the COpenTrapView class
  2. //
  3. // OpenTrap Version 1.00 by Gregory A. Wolking
  4. // Copyright ⌐ 1997 Ziff-Davis Publishing
  5. // First published in PC Magazine, US Edition, July 1997.
  6.  
  7. #include "stdafx.h"
  8. #include "OpenTrap.h"
  9. #include "MainFrm.h"
  10. #include "OTextern.h"
  11.  
  12. #include "OTDoc.h"
  13. #include "OTView.h"
  14. #ifdef _DEBUG
  15. #define new DEBUG_NEW
  16. #undef THIS_FILE
  17. static char THIS_FILE[] = __FILE__;
  18. #endif
  19.  
  20. /////////////////////////////////////////////////////////////////////////////
  21. // COpenTrapView
  22.  
  23. IMPLEMENT_DYNCREATE(COpenTrapView, CScrollView)
  24.  
  25. BEGIN_MESSAGE_MAP(COpenTrapView, CScrollView)
  26.     //{{AFX_MSG_MAP(COpenTrapView)
  27.     ON_WM_KEYDOWN()
  28.     //}}AFX_MSG_MAP
  29.     // Standard printing commands
  30.     ON_COMMAND(ID_FILE_PRINT, CScrollView::OnFilePrint)
  31.     ON_COMMAND(ID_FILE_PRINT_DIRECT, CScrollView::OnFilePrint)
  32.     ON_COMMAND(ID_FILE_PRINT_PREVIEW, CScrollView::OnFilePrintPreview)
  33. END_MESSAGE_MAP()
  34.  
  35. /////////////////////////////////////////////////////////////////////////////
  36. // COpenTrapView construction/destruction
  37.  
  38. COpenTrapView::COpenTrapView()
  39. {
  40.     CWinApp* pApp = AfxGetApp();
  41.     CString buf1, buf2;
  42.  
  43.     m_pFiltered_Recs = NULL;
  44.     m_intPage_Count = 0;
  45.     m_intCurrent_Page = 0;
  46.     m_fontCustom = NULL;
  47.     m_intLines_Per_Page = 1;
  48.     buf1 = pApp->GetProfileString(g_szFontKey, "Font Data", "***");
  49.     buf2 = pApp->GetProfileString(g_szFontKey, "Face", "***");
  50.     if ((buf1.Compare("***") != 0) && (buf2.Compare("***") != 0))
  51.     {
  52.         int args;
  53.         LOGFONT font;
  54.  
  55.         args = sscanf(buf1, g_szFontFormat, 
  56.             &font.lfHeight, &font.lfWidth, &font.lfEscapement,
  57.             &font.lfOrientation, &font.lfWeight, &font.lfItalic,
  58.             &font.lfUnderline, &font.lfStrikeOut, &font.lfCharSet,
  59.             &font.lfOutPrecision, &font.lfClipPrecision, &font.lfQuality,
  60.             &font.lfPitchAndFamily);
  61.         if (args == 13)
  62.         {
  63.             lstrcpy(font.lfFaceName, buf2);
  64.             m_fontCustom = new CFont;
  65.             if (!m_fontCustom->CreateFontIndirect(&font))
  66.             {
  67.                 delete m_fontCustom;
  68.                 m_fontCustom = NULL;
  69.             }
  70.         }
  71.     }
  72.     // Set default scroll sizes so initial WM_PAINT doesn't barf.
  73.     SetScrollSizes(MM_TEXT, CSize(100, 100));
  74. }
  75.  
  76. COpenTrapView::~COpenTrapView()
  77. {
  78.     if (m_pFiltered_Recs)
  79.     {
  80.         free(m_pFiltered_Recs);
  81.         m_pFiltered_Recs = NULL;
  82.     }
  83.     if (m_fontCustom)    // If custom font is in use,
  84.     {
  85.         LOGFONT font;        // Save font information to the Registry.
  86.         CString font_data;
  87.         m_fontCustom->GetLogFont(&font);
  88.         font_data.Format(g_szFontFormat,
  89.             font.lfHeight, font.lfWidth, font.lfEscapement,
  90.             font.lfOrientation, font.lfWeight, font.lfItalic,
  91.             font.lfUnderline, font.lfStrikeOut, font.lfCharSet,
  92.             font.lfOutPrecision, font.lfClipPrecision, font.lfQuality,
  93.             font.lfPitchAndFamily);
  94.         AfxGetApp()->WriteProfileString(g_szFontKey, "Font Data", font_data);
  95.         AfxGetApp()->WriteProfileString(g_szFontKey, "Face", font.lfFaceName);
  96.         delete m_fontCustom;
  97.         m_fontCustom = NULL;
  98.     }
  99.     else    // Otherwise, delete font information from the Registry.
  100.         RegDeleteKey(HKEY_CURRENT_USER, "Software\\PC Magazine\\OpenTrap\\1.0\\View\\Font");
  101. }
  102.  
  103. BOOL COpenTrapView::PreCreateWindow(CREATESTRUCT& cs)
  104. {
  105.     return CScrollView::PreCreateWindow(cs);
  106. }
  107.  
  108. /////////////////////////////////////////////////////////////////////////////
  109. // COpenTrapView drawing
  110. void COpenTrapView::OnDraw(CDC* pDC)
  111. {
  112.     CFont* old_font = NULL;                                // Pointer to save current font if needed.
  113.     COpenTrapDoc* pDoc = GetDocument();                    // Get pointer to current document.
  114.     COLORREF clr_norm = GetSysColor(COLOR_WINDOW);      // Background color for normal events
  115.     COLORREF clr_err = GetSysColor(COLOR_WINDOWTEXT);    // Background color for error events
  116.     CRect my_rect;
  117.     CString text;
  118.     if (m_fontCustom)                                    // If custom font is active,
  119.         old_font = pDC->SelectObject(m_fontCustom);        // select it into the device context.
  120.     CSize text_size = pDC->GetTextExtent("M", 1);        // Get size of an "em" in the current font.
  121.     int cur_y = 0;
  122.  
  123.     if (!pDC->IsPrinting())                // If we're not printing,
  124.     {
  125.         if (g_bIsLogging || g_intRecCount == 0)        // If logging or log is empty,
  126.         {
  127.             if (old_font)
  128.                 pDC->SelectObject(old_font);
  129.             return;                                    // nothing to draw.
  130.         }
  131.         if (m_intFilt_Rec_Count == 0)                    // If no records filtered,
  132.         {
  133.             text = "No matching records found.";    // display appropriate message
  134.             pDC->TextOut(5, cur_y, text);
  135.             if (old_font)
  136.                 pDC->SelectObject(old_font);
  137.             return;                                    // and we're done.
  138.         }
  139.  
  140.     // We have records to display, so get to it!
  141.         char outbuf[512];
  142.         CSize sizeTotal, sizePage, sizeLine;
  143.         CPoint scroll_pos = GetScrollPosition();    // Find where the scroll thumb is.
  144.         int view_top, view_bot, max_width = 0;
  145.         UINT cur_rec;
  146.         GetWindowRect(my_rect);                        // Get window coordinates.
  147.         my_rect.NormalizeRect();
  148.         // Determine the region we need to draw: from one full line above the
  149.         // window to one full line below it.
  150.         if (scroll_pos.y > text_size.cy)
  151.             view_top = (scroll_pos.y - text_size.cy) - (scroll_pos.y % text_size.cy);
  152.         else
  153.             view_top = 0;
  154.         view_bot = view_top + my_rect.Height() + text_size.cy * 2;
  155.         // Calculate which record is at the top of the drawing region.
  156.         cur_y = view_top;
  157.         cur_rec = (m_intCurrent_Page - 1) * m_intLines_Per_Page + (view_top / text_size.cy);
  158.         // Loop until we either reach the end of the viewport or run out of records.
  159.         while(cur_y < view_bot && cur_rec < m_intFilt_Rec_Count)
  160.         {
  161.             // Decode record to text buffer.
  162.             pDoc->Packed_to_ASCII(m_pFiltered_Recs[cur_rec].rec_num,
  163.                 m_pFiltered_Recs[cur_rec].pRec, outbuf);
  164.             if (m_pFiltered_Recs[cur_rec].pRec->pr_error)    // Set appropriate background color.
  165.             {
  166.                 pDC->SetTextColor(clr_norm);
  167.                 pDC->SetBkColor(clr_err);
  168.             }
  169.             else
  170.             {
  171.                 pDC->SetBkColor(clr_norm);
  172.                 pDC->SetTextColor(clr_err);
  173.             }
  174.             text_size = pDC->GetTextExtent(outbuf, strlen(outbuf) - 2);    // Calculate size of text.
  175.             if (text_size.cx > max_width)            // Update max width (used for setting horizontal scroll)
  176.                 max_width = text_size.cx;
  177.             pDC->TextOut(5, cur_y, outbuf, strlen(outbuf) - 2);        // Draw the text.
  178.             cur_y += text_size.cy;        // Update position.
  179.             ++cur_rec;                    // Next record.
  180.         }
  181.         // Calculate and set scroll sizes.
  182.         sizePage.cy = my_rect.Height() - (my_rect.Height() % text_size.cy);
  183.         sizeLine.cy = text_size.cy;
  184.         m_sizeCurrent_Page.cx = max_width + 10;
  185.         sizePage.cx = my_rect.Width();
  186.         sizeLine.cx = sizePage.cx / 10;
  187.         SetScrollSizes(MM_TEXT, m_sizeCurrent_Page, sizePage, sizeLine);
  188.     }
  189.     else    // We're printing!
  190.     {
  191.         int line_y, left_margin, left_indent, targ_x, len;
  192.         UINT cur_rec;
  193.         CSize check_size;
  194.         CRect page_rect;
  195.         char outbuf[512], *p1, *p2;
  196.         BOOL new_page = FALSE, first_line;
  197.  
  198.         // Set mapping mode. Note: in LOENGLISH mode, y decreases as we move down the page.
  199.         // This mode scales the font using logical units instead of pixels (as in MM_TEXT mode
  200.         // used for screen output) so the font is the same size regardless of printer resolution.
  201.         pDC->SetMapMode(MM_LOENGLISH);
  202.         // Get page rectangle in pixels.
  203.         page_rect = CRect(0, 0, pDC->GetDeviceCaps(HORZRES), pDC->GetDeviceCaps(VERTRES));
  204.         pDC->DPtoLP(page_rect);                        // Convert page rectangle to logical units.
  205.         text_size = pDC->GetTextExtent("M", 1);        // Get size of an "em" in the current font.
  206.         line_y = text_size.cy;                        // Set height of one line.
  207.         page_rect.top -= (line_y * 2);                // Top margin of two lines.
  208.         page_rect.bottom -= (line_y * 2);            // Bottom margin of two lines.
  209.         clr_err = RGB(128, 128, 128);        // Set error color to 50% gray.
  210.         clr_norm = RGB(255, 255, 255);        // Normal color is white for printing.
  211.         pDC->SetTextColor(RGB(0, 0, 0));    // Text color for printing is black
  212.         pDC->SetBkColor(clr_norm);            // on a white background.
  213.         left_margin = text_size.cx;            // Set left margin of one em.
  214.         left_indent = text_size.cx * 3;        // Left indent of 3 ems.
  215.         page_rect.right -= text_size.cx;    // Right margin of 1 em.
  216.  
  217.     // Now that we've got all of our dimensions calculated, draw records on the display context.
  218.         cur_y = page_rect.top;        // Start at the top of the page.
  219.         for (cur_rec = 0; cur_rec < m_intFilt_Rec_Count; ++cur_rec)
  220.         {
  221.             if (new_page)                    // If flag is set,
  222.             {
  223.                 cur_y = page_rect.top;            // Back to top of page.
  224.                 new_page = FALSE;                // Clear flag.
  225.                 Start_New_Page(pDC, old_font);                // End current page and start a new page.
  226.             }
  227.             // Decode record to text buffer.
  228.             pDoc->Packed_to_ASCII(m_pFiltered_Recs[cur_rec].rec_num, m_pFiltered_Recs[cur_rec].pRec, outbuf);
  229.             // Set appropriate background color.
  230.             if (m_pFiltered_Recs[cur_rec].pRec->pr_error)
  231.                 pDC->SetBkColor(clr_err);
  232.             else
  233.                 pDC->SetBkColor(clr_norm);
  234.             // Calculate size of text, ignoring trailing CR/LF.
  235.             text_size = pDC->GetTextExtent(outbuf, strlen(outbuf) - 2);
  236.             p1 = outbuf;
  237.             first_line = TRUE;
  238.             // As long as text is too wide for one line,
  239.             while (text_size.cx + (first_line ? left_margin : left_indent) > page_rect.right)
  240.             {
  241.                 p2 = p1 + strlen(p1) - 3;    // Point to last character.
  242.                 while (TRUE)
  243.                 {    // Search backwards for either a space or a backslash.
  244.                     while(*p2 != ' ' && *p2 != '\\' && p2 >= p1)
  245.                         --p2;
  246.                     // If we can't find a logical point at which to break the line,
  247.                     if (p2 == p1)
  248.                     {    // Resort to brute force -- get target line length.
  249.                         targ_x = page_rect.right - (first_line ? left_margin : left_indent);
  250.                         len = 1;
  251.                         // Start with first character (p2 already points there).
  252.                         // Loop until end of buffer is reached.
  253.                         while (TRUE)
  254.                         {    // Get width line up to current character.
  255.                             check_size = pDC->GetTextExtent(p1, len);
  256.                             if (check_size.cx < targ_x)    // Does it fit?
  257.                                 ++len;    // add another character if so.
  258.                             else
  259.                             {    // otherwise back up to the last character that _did_ fit.
  260.                                 --len;
  261.                                 break;
  262.                             }
  263.                         }
  264.                         p2 = p1 + len;    // Point to end of text.
  265.                         break;            // Done adjusting.
  266.                     }
  267.                     // If logical break point found, get size of text up to that point.
  268.                     check_size = pDC->GetTextExtent(p1, (p2 - p1) + 1);
  269.                     if ((check_size.cx + (first_line ? left_margin : left_indent)) <= page_rect.right)
  270.                         break;    // Done adjusting if small enough.
  271.                     else
  272.                         --p2;    // Otherwise, back up one character and keep looking.
  273.                 }
  274.                 // Draw the part of the text that fits.
  275.                 pDC->TextOut((first_line ? left_margin : left_indent), cur_y, p1, (p2 - p1) + 1);
  276.                 first_line = FALSE;             // Clear first line flag.
  277.                 cur_y -= line_y;                // Update position.
  278.                 if ((cur_y - line_y) < page_rect.bottom)    // Room for another line?
  279.                 {
  280.                     cur_y = page_rect.top;        // New page if not.
  281.                     Start_New_Page(pDC, old_font);
  282.                 }
  283.                 p1 = p2 + 1;                    // Move past the text we just printed.
  284.                 text_size = pDC->GetTextExtent(p1, strlen(p1) - 2);                // Get size of remainder.
  285.             }
  286.             // Draw all text remaining after any adjustments.
  287.             pDC->TextOut((first_line ? left_margin : left_indent),
  288.                 cur_y, p1, strlen(p1) - 2);
  289.             cur_y -= line_y;    // Update page position.
  290.             new_page = ((cur_y - line_y) < page_rect.bottom);    // Set flag if not enough room for another line.
  291.         }
  292.     }
  293.     if (old_font)
  294.         pDC->SelectObject(old_font);
  295. }
  296.  
  297.  
  298.  
  299.  
  300. void COpenTrapView::OnInitialUpdate()
  301. {
  302.     CScrollView::OnInitialUpdate();
  303. }
  304.  
  305. /////////////////////////////////////////////////////////////////////////////
  306. // COpenTrapView printing
  307.  
  308. BOOL COpenTrapView::OnPreparePrinting(CPrintInfo* pInfo)
  309. {
  310.     // Disable the Print Dialog's "Page Number" and "Selection" radio buttons.
  311.     pInfo->m_pPD->m_pd.Flags |= (PD_NOPAGENUMS | PD_NOSELECTION);
  312.     return DoPreparePrinting(pInfo);
  313. }
  314.  
  315. void COpenTrapView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
  316. {
  317.     // TODO: add extra initialization before printing
  318. }
  319.  
  320. void COpenTrapView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
  321. {
  322.     // TODO: add cleanup after printing
  323. }
  324.  
  325. /////////////////////////////////////////////////////////////////////////////
  326. // COpenTrapView diagnostics
  327.  
  328. #ifdef _DEBUG
  329. void COpenTrapView::AssertValid() const
  330. {
  331.     CScrollView::AssertValid();
  332. }
  333.  
  334. void COpenTrapView::Dump(CDumpContext& dc) const
  335. {
  336.     CScrollView::Dump(dc);
  337. }
  338.  
  339. COpenTrapDoc* COpenTrapView::GetDocument() // non-debug version is inline
  340. {
  341.     ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(COpenTrapDoc)));
  342.     return (COpenTrapDoc*)m_pDocument;
  343. }
  344. #endif //_DEBUG
  345.  
  346. /////////////////////////////////////////////////////////////////////////////
  347. // COpenTrapView message handlers
  348.  
  349. // Called when document is updated.
  350. // If lHint is non-zero, the "update" is simply
  351. // the user selecting a different page, and lHint contains the
  352. // identifier of the page command.
  353. void COpenTrapView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) 
  354. {
  355.     Calc_Scroll_Sizes((BOOL) lHint);
  356.     CScrollView::OnUpdate(pSender, lHint, pHint);
  357. }
  358.  
  359. // Sets the parameters for the scroll bar.
  360. // Called when document is updated.
  361. // If update is an actual change to the document (e.g., log records added),
  362. // the list of filtered records is rebuilt.
  363. void COpenTrapView::Calc_Scroll_Sizes(BOOL page_change)
  364. {
  365.     CSize size;
  366.     if (g_bIsLogging || g_intRecCount == 0)
  367.     {
  368.         size.cx = size.cy = 100;
  369.         SetScrollSizes(MM_TEXT, size);
  370.         return;
  371.     }
  372.     if (!page_change)            // Did document actually change?
  373.         Build_Record_List();    // Rebuild record list if so.
  374.     CDC* pDC = GetDC();            // Get pointer to device context.
  375.     CFont* old_font = NULL;
  376.     if (m_fontCustom)
  377.         old_font = pDC->SelectObject(m_fontCustom);
  378.     size = pDC->GetTextExtent("M",1);    // Get height of one line.
  379.     if (old_font)                        // Restore original font if necessary.
  380.         pDC->SelectObject(old_font);
  381.     m_intLines_Per_Page = 16384 / size.cy;    // Get # of lines per page.
  382.     if (m_intLines_Per_Page > 1024)            // Max is 1024 lines.
  383.         m_intLines_Per_Page = 1024;
  384.     // Get number of full pages.
  385.     m_intPage_Count = (m_intFilt_Rec_Count / m_intLines_Per_Page);
  386.     // Add one page if record count is not an even multiple of the line count.
  387.     if (m_intFilt_Rec_Count % m_intLines_Per_Page != 0)
  388.         ++m_intPage_Count;
  389.     // Set vertical size of current page.
  390.     if (m_intFilt_Rec_Count > (m_intCurrent_Page * m_intLines_Per_Page))    // If current page is full,
  391.         size.cy = (size.cy * m_intLines_Per_Page);        // Page height is max lines.
  392.     else                                // Otherwise,
  393.         size.cy = size.cy * (m_intFilt_Rec_Count % m_intLines_Per_Page); // Height is number of lines in the page.
  394.     m_sizeCurrent_Page = size;
  395.     SetScrollSizes(MM_TEXT, size);
  396.     // If scrolling to previous or last page,
  397.     // force scroll position to the bottom of the page.
  398.     if (page_change == ID_VIEW_PREVIOUS || page_change == ID_VIEW_LAST)
  399.         SetScrollPos(SB_VERT, size.cy, FALSE);
  400. }
  401.  
  402. // Handles keyboard scrolling within the current page.
  403. // Note that page navigation commands (Ctrl + key) are intercepted
  404. // as accelerators, so they never appear as OnKeyDown events.
  405. void COpenTrapView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
  406. {
  407.     UINT msg = 0;
  408.     WPARAM func = 0;
  409.  
  410.     switch (nChar)    // Determine the scroll message and function.
  411.     {
  412.     case VK_DOWN:    msg = WM_VSCROLL;    func = SB_LINEDOWN;    break;
  413.     case VK_UP:        msg = WM_VSCROLL;    func = SB_LINEUP;    break;
  414.     case VK_NEXT:    msg = WM_VSCROLL;    func = SB_PAGEDOWN;    break;
  415.     case VK_PRIOR:    msg = WM_VSCROLL;    func = SB_PAGEUP;    break;
  416.     case VK_HOME:    msg = WM_VSCROLL;    func = SB_TOP;        break;
  417.     case VK_END:    msg = WM_VSCROLL;    func = SB_BOTTOM;    break;
  418.     case VK_LEFT:    msg = WM_HSCROLL;    func = SB_LINELEFT;    break;
  419.     case VK_RIGHT:    msg = WM_HSCROLL;    func = SB_LINERIGHT;
  420.     }
  421.     if (msg)                            // If keystroke was a scroll command,
  422.         PostMessage(msg, func, NULL);    // post the scroll message.
  423.     CScrollView::OnKeyDown(nChar, nRepCnt, nFlags);
  424. }
  425.  
  426. // Builds array of pointers to filtered records and sets number
  427. // of pages in the filtered view.
  428. void COpenTrapView::Build_Record_List(void)
  429. {
  430.     COpenTrapDoc * pDoc = GetDocument();
  431.     packed_record * pr = (packed_record*) g_pBufferStart;
  432.     UINT current = 0;
  433.  
  434.     m_intFilt_Rec_Count = 0;        // Reset filtered record count.
  435.     // Make sure array is big enough to hold entries for all records.
  436.     m_pFiltered_Recs = (filter_list *) realloc(m_pFiltered_Recs, g_intRecCount * sizeof(filter_list));
  437.     while (pr)                                    // For each record:
  438.     {
  439.         ++current;                                        // Bump absolute record number.
  440.         if (pDoc->Filter_Record(pr))                    // If record passes the filters,
  441.         {                                
  442.             m_pFiltered_Recs[m_intFilt_Rec_Count].pRec = pr;            // add it to the array,
  443.             m_pFiltered_Recs[m_intFilt_Rec_Count].rec_num = current;    // save absolute record number,
  444.             ++m_intFilt_Rec_Count;                                        // and update the count.
  445.         }
  446.         pr = pr->next_record;                    // Next record.
  447.     }
  448.     m_intPage_Count = m_intFilt_Rec_Count / m_intLines_Per_Page;    // Set number of pages
  449.     if (m_intFilt_Rec_Count % m_intLines_Per_Page != 0)                // Adjust if not an even multiple.
  450.         ++m_intPage_Count;
  451.     m_intCurrent_Page = 1;    // Set current page to 1.
  452.     // Release any memory not actually required for active array elements.
  453.     if (m_intFilt_Rec_Count)
  454.         m_pFiltered_Recs = (filter_list*) realloc(m_pFiltered_Recs, m_intFilt_Rec_Count * sizeof(filter_list));
  455.     else
  456.     {
  457.         free(m_pFiltered_Recs);
  458.         m_pFiltered_Recs = NULL;
  459.     }
  460. }
  461.  
  462. // Called by OnDraw to end the current page and start a new one.
  463. void COpenTrapView::Start_New_Page(CDC* pDC, CFont* old_font)
  464. {
  465.     pDC->EndPage();
  466.     pDC->StartPage();
  467.     pDC->SetMapMode(MM_LOENGLISH);
  468.     if (m_fontCustom)
  469.         old_font = pDC->SelectObject(m_fontCustom);
  470. }
  471.